package com.smh.yunpicturebackend.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.benmanes.caffeine.cache.Cache;
import com.smh.yunpicturebackend.api.aliyunai.AliYunAiApi;
import com.smh.yunpicturebackend.api.aliyunai.model.CreateOutPaintingTaskRequest;
import com.smh.yunpicturebackend.api.aliyunai.model.CreateOutPaintingTaskResponse;
import com.smh.yunpicturebackend.cache.CacheManger;
import com.smh.yunpicturebackend.exception.BusinessException;
import com.smh.yunpicturebackend.exception.ErrorCode;
import com.smh.yunpicturebackend.manager.upload.PictureUploadTemplate;
import com.smh.yunpicturebackend.mapper.PictureMapper;
import com.smh.yunpicturebackend.pojo.constant.CacheConstant;
import com.smh.yunpicturebackend.pojo.constant.FileConstant;
import com.smh.yunpicturebackend.pojo.domain.Picture;
import com.smh.yunpicturebackend.pojo.domain.Space;
import com.smh.yunpicturebackend.pojo.domain.User;
import com.smh.yunpicturebackend.pojo.dto.picture.*;
import com.smh.yunpicturebackend.pojo.enums.PictureReviewStatusEnum;
import com.smh.yunpicturebackend.pojo.vo.picture.PictureVO;
import com.smh.yunpicturebackend.pojo.vo.user.UserVO;
import com.smh.yunpicturebackend.service.PictureService;
import com.smh.yunpicturebackend.service.SpaceService;
import com.smh.yunpicturebackend.service.UserService;
import com.smh.yunpicturebackend.utils.AliyunOSSUtils;
import com.smh.yunpicturebackend.utils.ColorUtils;
import com.smh.yunpicturebackend.utils.ThrowUtils;
import com.smh.yunpicturebackend.utils.UserThreadLocal;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author shiminghui
 * @description 针对表【picture(图片)】的数据库操作Service实现
 * @createDate 2025-06-19 23:38:23
 */
@Service
@Slf4j
public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture>
        implements PictureService {

    @Resource
    private UserService userService;
    @Resource
    @Lazy
    private SpaceService spaceService;
    @Resource
    private PictureUploadTemplate pictureUpload;
    @Resource
    private PictureUploadTemplate urlPictureUpload;
    @Resource
    private RedisTemplate stringRedisTemplate;
    @Resource
    private CacheManger cacheManger;
    @Resource
    private AliyunOSSUtils aliyunOSSUtils;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private PictureMapper pictureMapper;
    @Resource
    private AliYunAiApi aliYunAiApi;
    // 为了实现异步执行，需要注入自己
    @Autowired
    @Lazy
    private PictureService pictureService;

    @Override
    public PictureVO uploadPicture(Object inputSource, PictureUploadRequest pictureUploadRequest) {
        // 参数校验
        if (inputSource == null || pictureUploadRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 获取当前用户
        User user = UserThreadLocal.get();
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_LOGIN_ERROR);

        // 检验图片是否存在以及空间是否可操作
        Picture oldPicture = checkSpaceIdAndPictureId(pictureUploadRequest);

        // 判断使用哪一个图片上传模板
        PictureUploadTemplate picUploadTemplate = pictureUpload;
        if (inputSource instanceof String) {
            picUploadTemplate = urlPictureUpload;
        }

        // 上传图片
        UploadPictureResult pictureResult = null;
        if (pictureUploadRequest.getSpaceId() == null) {
            pictureResult = picUploadTemplate.uploadPicture(FileConstant.PUBLIC_FILE, inputSource);
        } else {
            pictureResult = picUploadTemplate.uploadPicture(FileConstant.PRIVATE_FILE, inputSource);
        }
        // 将图片压缩
        String sourceUrl = pictureResult.getSourceUrl();
        String url = aliyunOSSUtils.formatFileTypeAndSave(sourceUrl);
        // 拿到压缩后的图片信息
        AliyunOSSUtils.BaseImageInfo baseImageInfo = aliyunOSSUtils.getBaseImageInfo(url);
        // 只有大于100kB的图片才进行缩略(默认为缩略图)
        String thumbnailUrl = url;
        if (baseImageInfo.getFileSize() > FileConstant.MIN_THUMBNAIL_SIZE) {
            // 将图片缩略
            thumbnailUrl = aliyunOSSUtils.thumbnailFileAndSave(sourceUrl, 320, 180);
        }

        // 构建 picture 对象
        Picture picture = bulidPicture(pictureUploadRequest, url, sourceUrl, thumbnailUrl, pictureResult, user, oldPicture);

        // 保存或更新picture
        transactionTemplate.execute(status -> {
            boolean b = this.saveOrUpdate(picture);
            ThrowUtils.throwIf(!b, ErrorCode.OPERATION_ERROR, "上传图片失败,保存到数据库失败");
            if (pictureUploadRequest.getSpaceId() != null && oldPicture != null) {
                // 更新空间大小和图片数量
                boolean update = spaceService.updatePictureToSpace(pictureUploadRequest.getSpaceId(), picture, oldPicture);
                ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "更新空间大小和图片数量失败");
            } else if (pictureUploadRequest.getSpaceId() != null) {
                // 新增空间大小和图片数量
                boolean update = spaceService.addPictureToSpace(pictureUploadRequest.getSpaceId(), picture);
                ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "更新空间大小和图片数量失败");
            }
            return null;
        });

        // 删除旧的图片
        if (oldPicture != null) {
            pictureService.deletePictureFile(oldPicture);
//            this.deletePictureFile(oldPicture);
        }
        return PictureVO.objToVo(picture);
    }


    /**
     * 构建picture对象
     *
     * @param pictureUploadRequest 上传图片的请求
     * @param url                  压缩图片的url
     * @param sourceUrl            原图片的url
     * @param thumbnailUrl         缩略图片的url
     * @param pictureResult        上传图片返回的图片信息
     * @param user                 当前用户
     * @param oldPicture           旧的图片
     * @return picture对象
     */
    private Picture bulidPicture(PictureUploadRequest pictureUploadRequest, String url, String sourceUrl, String thumbnailUrl, UploadPictureResult pictureResult, User user, Picture oldPicture) {
        // 创建picture
        Picture picture = new Picture();
        // 设置 url
        picture.setUrl(url);
        picture.setSourceUrl(sourceUrl);
        picture.setThumbnailUrl(thumbnailUrl);

        // 如果用户自定义了图片名,则使用自定义的图片名
        String pictureName = pictureResult.getPicName();
        if (pictureUploadRequest != null && StrUtil.isNotBlank(pictureUploadRequest.getPicName())) {
            pictureName = pictureUploadRequest.getPicName();
        }

        picture.setName(pictureName);
        picture.setPicFormat(pictureResult.getPicFormat());
        picture.setPicSize(pictureResult.getPicSize());
        picture.setPicHeight(pictureResult.getPicHeight());
        picture.setPicWidth(pictureResult.getPicWidth());
        picture.setPicScale(pictureResult.getPicScale());
        picture.setUserId(user.getId());

        // 设置图片颜色信息
        ColorUtils.Color color = ColorUtils.getColor(pictureResult.getPicColor());
        picture.setRed(color.getRed());
        picture.setGreen(color.getGreen());
        picture.setBlue(color.getBlue());

        // 填充审核参数
        fillReviewParams(picture);

        if (oldPicture != null) {
            picture.setId(oldPicture.getId());
            picture.setEditTime(new Date());
        }
        if (pictureUploadRequest.getSpaceId() != null) {
            picture.setSpaceId(pictureUploadRequest.getSpaceId());
        }
        return picture;
    }

    /**
     * 校验空间是否存在并且是否有权限上传/修改
     *
     * @param pictureUploadRequest dto 封装类 包含 user
     * @return 旧图片信息
     */
    private Picture checkSpaceIdAndPictureId(PictureUploadRequest pictureUploadRequest) {
        User user = UserThreadLocal.get();
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_LOGIN_ERROR);
        Long spaceId = pictureUploadRequest.getSpaceId();
        Long pictureId = pictureUploadRequest.getId();

        Picture picture = null;

        // 校验空间是否存在并且是否有权限上传/修改
        if (spaceId != null) {
            Space space = spaceService.getById(spaceId);
            picture = getById(pictureId);
            // 这里已经过注解校验过了
//            ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
//            // 必须空间创建人（管理员）才能上传，这个管理员指的是创建空间人
//            ThrowUtils.throwIf(!space.getUserId().equals(user.getId()), ErrorCode.NO_AUTH_ERROR, "空间创建人才能上传图片");
//            // 查询图片数据,判断是否位于space空间内
//            if (pictureId != null) {
//                picture = getById(pictureId);
//                ThrowUtils.throwIf(!picture.getSpaceId().equals(spaceId), ErrorCode.NO_AUTH_ERROR, "图片不属于该空间");
//            }
            if (space.getTotalSize() >= space.getMaxSize()) {
                ThrowUtils.throwIf(true, ErrorCode.OPERATION_ERROR, "空间已满");
            }
            if (space.getTotalCount() >= space.getMaxCount()) {
                ThrowUtils.throwIf(true, ErrorCode.OPERATION_ERROR, "文件数量已满");
            }
        } else if (pictureId != null) {
            // 未传入spaceId,且pictureId不为空,则查询到该图片之前所在的空间,更新它
            picture = getById(pictureId);
            ThrowUtils.throwIf(picture == null, ErrorCode.NOT_FOUND_ERROR, "图片不存在");
            ThrowUtils.throwIf(!user.getId().equals(picture.getUserId()) && !userService.isAdmin(), ErrorCode.NO_AUTH_ERROR);
            pictureUploadRequest.setSpaceId(picture.getSpaceId());
        }
        return picture;
    }


    /**
     * 删除旧的图片
     *
     * @param oldPicture 旧图片
     */
    @Override
    @Async("lightTaskExecutor") // 异步执行,使用轻量级线程池
    public void deletePictureFile(Picture oldPicture) {
        List<String> oldPictureUrls = new ArrayList<>();
        if (StrUtil.isNotBlank(oldPicture.getUrl())) {
            oldPictureUrls.add(oldPicture.getUrl());
        }
        if (StrUtil.isNotBlank(oldPicture.getThumbnailUrl())) {
            oldPictureUrls.add(oldPicture.getThumbnailUrl());
        }
        if (StrUtil.isNotBlank(oldPicture.getSourceUrl())) {
            oldPictureUrls.add(oldPicture.getSourceUrl());
        }
        Integer deleteCount = aliyunOSSUtils.deleteFiles(oldPictureUrls);
        log.info("预期删除{}张图片,实际删除{}张图片", oldPictureUrls.size(), deleteCount);
    }

    @Override
    public QueryWrapper<Picture> getQueryWrapper(PictureQueryRequest pictureQueryRequest) {
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        if (pictureQueryRequest == null) {
            return queryWrapper;
        }
        // 从对象中取值
        Long id = pictureQueryRequest.getId();
        String name = pictureQueryRequest.getName();
        String introduction = pictureQueryRequest.getIntroduction();
        String category = pictureQueryRequest.getCategory();
        List<String> tags = pictureQueryRequest.getTags();
        Long picSize = pictureQueryRequest.getPicSize();
        Integer picWidth = pictureQueryRequest.getPicWidth();
        Integer picHeight = pictureQueryRequest.getPicHeight();
        Double picScale = pictureQueryRequest.getPicScale();
        String picFormat = pictureQueryRequest.getPicFormat();
        String searchText = pictureQueryRequest.getSearchText();
        Long userId = pictureQueryRequest.getUserId();
        String sortField = pictureQueryRequest.getSortField();
        String sortOrder = pictureQueryRequest.getSortOrder();
        Integer reviewStatus = pictureQueryRequest.getReviewStatus();
        String reviewMessage = pictureQueryRequest.getReviewMessage();
        Long reviewerId = pictureQueryRequest.getReviewerId();
        Long spaceId = pictureQueryRequest.getSpaceId();
        boolean nullSpaceId = pictureQueryRequest.isNullSpaceId();
        Date startEditTime = pictureQueryRequest.getStartEditTime();
        Date endEditTime = pictureQueryRequest.getEndEditTime();

        // 从多字段中搜索
        if (StrUtil.isNotBlank(searchText)) {
            // 需要拼接查询条件
            queryWrapper.and(qw -> qw.like("name", searchText)
                    .or()
                    .like("introduction", searchText)
            );
        }
        queryWrapper.eq(ObjUtil.isNotEmpty(id) && id > 0, "id", id);
        queryWrapper.eq(ObjUtil.isNotEmpty(userId) && userId > 0, "user_id", userId);
        queryWrapper.like(StrUtil.isNotBlank(name), "name", name);
        queryWrapper.like(StrUtil.isNotBlank(introduction), "introduction", introduction);
        queryWrapper.like(StrUtil.isNotBlank(picFormat), "pic_format", picFormat);
        queryWrapper.eq(StrUtil.isNotBlank(category), "category", category);
        queryWrapper.eq(ObjUtil.isNotEmpty(picWidth) && picWidth > 0, "pic_width", picWidth);
        queryWrapper.eq(ObjUtil.isNotEmpty(picHeight) && picHeight > 0, "pic_height", picHeight);
        queryWrapper.eq(ObjUtil.isNotEmpty(picSize) && picSize > 0, "pic_size", picSize);
        queryWrapper.eq(ObjUtil.isNotEmpty(picScale) && picScale > 0, "pic_scale", picScale);
        queryWrapper.eq(ObjUtil.isNotEmpty(reviewStatus), "review_status", reviewStatus);
        queryWrapper.like(StrUtil.isNotBlank(reviewMessage), "review_message", reviewMessage);
        queryWrapper.eq(ObjUtil.isNotEmpty(reviewerId) && reviewerId > 0, "reviewer_id", reviewerId);
        queryWrapper.eq(ObjUtil.isNotEmpty(spaceId), "space_id", spaceId); // 不为null,查找有值的记录
        queryWrapper.isNull(nullSpaceId, "space_id"); // 空间id为null,查找null的记录
        queryWrapper.ge(ObjUtil.isNotEmpty(startEditTime), "edit_time", startEditTime);
        queryWrapper.lt(ObjUtil.isNotEmpty(endEditTime), "edit_time", endEditTime);

        // JSON 数组查询
        if (CollUtil.isNotEmpty(tags)) {
            for (String tag : tags) {
                queryWrapper.like("tags", "\"" + tag + "\"");
            }
        }
        // 排序
        queryWrapper.orderBy(StrUtil.isNotEmpty(sortField), sortOrder.equals("ascend"), sortField);
        return queryWrapper;
    }

    @Override
    public PictureVO getPictureVO(Picture picture) {
        // 对象转封装类
        PictureVO pictureVO = PictureVO.objToVo(picture);
        // 关联查询用户信息
        Long userId = picture.getUserId();
        if (userId != null && userId > 0) {
            User user = userService.getById(userId);
            UserVO userVO = userService.getUserVO(user);
            pictureVO.setUser(userVO);
        }
        return pictureVO;
    }

    /**
     * 分页获取图片封装
     */
    @Override
    public Page<PictureVO> getPictureVOPage(Page<Picture> picturePage) {
        List<Picture> pictureList = picturePage.getRecords();
        Page<PictureVO> pictureVOPage = new Page<>(picturePage.getCurrent(), picturePage.getSize(), picturePage.getTotal());
        if (CollUtil.isEmpty(pictureList)) {
            return pictureVOPage;
        }
        // 对象列表 => 封装对象列表
        List<PictureVO> pictureVOList = pictureList.stream().map(PictureVO::objToVo).collect(Collectors.toList());
        // 1. 关联查询用户信息
        Set<Long> userIdSet = pictureList.stream().map(Picture::getUserId).collect(Collectors.toSet());
        Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.groupingBy(User::getId));
        // 2. 填充信息
        pictureVOList.forEach(pictureVO -> {
            Long userId = pictureVO.getUserId();
            User user = null;
            if (userIdUserListMap.containsKey(userId)) {
                user = userIdUserListMap.get(userId).get(0);
            }
            pictureVO.setUser(userService.getUserVO(user));
        });
        pictureVOPage.setRecords(pictureVOList);
        return pictureVOPage;
    }

    @Override
    public void validPicture(Picture picture) {
        ThrowUtils.throwIf(picture == null, ErrorCode.PARAMS_ERROR);
        // 从对象中取值
        Long id = picture.getId();
        String url = picture.getUrl();
        String introduction = picture.getIntroduction();
        // 修改数据时，id 不能为空，有参数则校验
        ThrowUtils.throwIf(ObjUtil.isNull(id), ErrorCode.PARAMS_ERROR, "id 不能为空");
        if (StrUtil.isNotBlank(url)) {
            ThrowUtils.throwIf(url.length() > 1024, ErrorCode.PARAMS_ERROR, "url 过长");
        }
        if (StrUtil.isNotBlank(introduction)) {
            ThrowUtils.throwIf(introduction.length() > 800, ErrorCode.PARAMS_ERROR, "简介过长");
        }
    }

    @Override
    public void doPictureReview(PictureReviewRequest pictureReviewRequest) {
        User user = UserThreadLocal.get();
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_LOGIN_ERROR);

        Long id = pictureReviewRequest.getId();
        Integer reviewStatus = pictureReviewRequest.getReviewStatus();
        PictureReviewStatusEnum reviewStatusEnum = PictureReviewStatusEnum.getEnumByValue(reviewStatus);
        if (id == null || reviewStatusEnum == null || PictureReviewStatusEnum.REVIEWING.equals(reviewStatusEnum)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 判断是否存在
        Picture oldPicture = this.getById(id);
        ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR);
        // 已是该状态
        if (oldPicture.getReviewStatus().equals(reviewStatus)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请勿重复审核");
        }
        // 更新审核状态
        Picture updatePicture = new Picture();
        BeanUtils.copyProperties(pictureReviewRequest, updatePicture);
        updatePicture.setReviewerId(user.getId());
        updatePicture.setReviewTime(new Date());
        boolean result = this.updateById(updatePicture);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
    }


    /**
     * 填充审核参数
     *
     * @param picture 图片
     */
    @Override
    public void fillReviewParams(Picture picture) {
        User user = UserThreadLocal.get();
        if (userService.isAdmin()) {
            // 管理员自动过审
            picture.setReviewStatus(PictureReviewStatusEnum.PASS.getValue());
            picture.setReviewerId(user.getId());
            picture.setReviewMessage("管理员自动过审");
            picture.setReviewTime(new Date());
        } else {
            // 非管理员，创建或编辑都要改为待审核
            picture.setReviewStatus(PictureReviewStatusEnum.REVIEWING.getValue());
        }
    }

    @Override
    public Integer uploadPictureByBatch(PictureUploadByBatchRequest pictureUploadByBatchRequest) {
        String searchText = pictureUploadByBatchRequest.getSearchText();
        String namePrefix = pictureUploadByBatchRequest.getNamePrefix();
        if (StrUtil.isBlank(namePrefix)) {
            namePrefix = searchText;
        }
        // 格式化数量
        Integer count = pictureUploadByBatchRequest.getCount();
        ThrowUtils.throwIf(count > 30, ErrorCode.PARAMS_ERROR, "最多 30 条");
        // 要抓取的地址
        String fetchUrl = String.format("https://cn.bing.com/images/async?q=%s&mmasync=1", searchText);
        Document document;
        try {
            document = Jsoup.connect(fetchUrl).get();
        } catch (IOException e) {
            log.error("获取页面失败", e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "获取页面失败");
        }
        Element div = document.getElementsByClass("dgControl").first();
        if (ObjUtil.isNull(div)) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "获取元素失败");
        }
        Elements imgElementList = div.select("img.mimg");
        int uploadCount = 0;
        for (Element imgElement : imgElementList) {
            String fileUrl = imgElement.attr("src");
            if (StrUtil.isBlank(fileUrl)) {
                log.info("当前链接为空，已跳过: {}", fileUrl);
                continue;
            }
            // 处理图片上传地址，防止出现转义问题
            int questionMarkIndex = fileUrl.indexOf("?");
            if (questionMarkIndex > -1) {
                fileUrl = fileUrl.substring(0, questionMarkIndex);
            }
            // 上传图片
            PictureUploadRequest pictureUploadRequest = new PictureUploadRequest();
            pictureUploadRequest.setFileUrl(fileUrl);
            pictureUploadRequest.setPicName(namePrefix + uploadCount);
            try {
                PictureVO pictureVO = this.uploadPicture(fileUrl, pictureUploadRequest);
                log.info("图片上传成功, id = {}", pictureVO.getId());
                uploadCount++;
            } catch (Exception e) {
                log.error("图片上传失败", e);
                continue;
            }
            if (uploadCount >= count) {
                break;
            }
        }
        return uploadCount;
    }

    /**
     * 基于缓存的图片列表查询
     *
     * @param pictureQueryRequest 查询请求参数
     * @return
     */
    @Override
    public Page<PictureVO> listPictureByPageWithCache(PictureQueryRequest pictureQueryRequest) {
        // 构建缓存 key
        String queryCondition = JSONUtil.toJsonStr(pictureQueryRequest);
        String hashKey = DigestUtils.md5DigestAsHex(queryCondition.getBytes());
        String localKey = "listPictureVOByPage:" + hashKey;
        String redisKey = "yupicture:listPictureVOByPage:" + hashKey;

        // 先从本地缓存查找
        Cache<String, String> pictureCache = (Cache<String, String>) cacheManger.getCache(CacheConstant.PICTURE_CACHE);
        String result = pictureCache.getIfPresent(localKey);
        if (result != null) {
            // 如果缓存命中，返回结果
            Page<PictureVO> cachedPage = JSONUtil.toBean(result, Page.class);
            return cachedPage;
        }

        // 从 Redis 缓存中查询
        ValueOperations<String, String> valueOps = stringRedisTemplate.opsForValue();
        String cachedValue = valueOps.get(redisKey);
        if (cachedValue != null) {
            // 如果缓存命中，返回结果
            Page<PictureVO> cachedPage = JSONUtil.toBean(cachedValue, Page.class);
            // 存入本地缓存
            pictureCache.put(localKey, cachedValue);
            return cachedPage;
        }

        // 查询数据库
        Page<Picture> picturePage = page(new Page<>(pictureQueryRequest.getCurrent(), pictureQueryRequest.getPageSize()),
                getQueryWrapper(pictureQueryRequest));
        // 获取封装类
        Page<PictureVO> pictureVOPage = getPictureVOPage(picturePage);

        // 存入 Redis 缓存
        String cacheValue = JSONUtil.toJsonStr(pictureVOPage);
        // 5 - 10 分钟随机过期，防止雪崩
        int cacheExpireTime = 300 + RandomUtil.randomInt(0, 300);
        valueOps.set(redisKey, cacheValue, cacheExpireTime, TimeUnit.SECONDS);

        // 存入本地缓存
        pictureCache.put(localKey, cacheValue);

        return pictureVOPage;
    }

    /**
     * 批量删除图片
     *
     * @param pictures
     * @return
     */
    @Override
    public boolean deletePictureFiles(List<Picture> pictures) {
        List<String> allFileUrls = getAllPictureUrls(pictures);
        Integer deleteCount = aliyunOSSUtils.deleteFiles(allFileUrls);
        log.info("预期删除{}张图片,实际删除了{}张图片.", allFileUrls.size(), deleteCount);
        return true;
    }

    /**
     * 获取所有图片的 URL
     *
     * @param pictures
     * @return
     */
    @Override
    public List<String> getAllPictureUrls(List<Picture> pictures) {
        return pictures.stream().map(picture -> {
            List<String> fileUrls = new ArrayList<>();
            if (StrUtil.isNotBlank(picture.getUrl())) {
                fileUrls.add(picture.getUrl());
            }
            if (StrUtil.isNotBlank(picture.getSourceUrl())) {
                fileUrls.add(picture.getSourceUrl());
            }
            if (StrUtil.isNotBlank(picture.getThumbnailUrl())) {
                fileUrls.add(picture.getThumbnailUrl());
            }
            return fileUrls;
        }).flatMap(Collection::stream).collect(Collectors.toList());
    }

    /**
     * 删除图片
     *
     * @param pictureId pictureId
     */
    @Override
    public void deletePicture(long pictureId) {
        ThrowUtils.throwIf(pictureId <= 0, ErrorCode.PARAMS_ERROR);
        User user = UserThreadLocal.get();
        ThrowUtils.throwIf(user == null, ErrorCode.NO_AUTH_ERROR);
        // 判断是否存在
        Picture oldPicture = this.getById(pictureId);
        ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR);
        // 校验权限
//        checkPictureAuth(oldPicture);
        transactionTemplate.execute(status -> {
            // 操作数据库
            boolean result = this.removeById(pictureId);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
            if (oldPicture.getSpaceId() != null) {
                boolean b = spaceService.deletePictureFromSpace(oldPicture.getSpaceId(), oldPicture);
                ThrowUtils.throwIf(!b, ErrorCode.OPERATION_ERROR);
            }
            return null;
        });
        // 异步清理文件
        pictureService.deletePictureFile(oldPicture);
//        this.deletePictureFile(oldPicture);
    }

    /**
     * 检查图片权限
     *
     * @param picture 图片
     */
    @Override
    @Deprecated // 已经通过权限拦截器实现
    public void checkPictureAuth(Picture picture) {
        User user = UserThreadLocal.get();
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_LOGIN_ERROR);

        Long spaceId = picture.getSpaceId();
        if (spaceId == null) {
            // 公共图库，仅本人或管理员可操作
            if (!picture.getUserId().equals(user.getId()) && !userService.isAdmin()) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
        }
        if (spaceId != null) {
            // 私有空间，仅空间管理员可操作
            if (!picture.getUserId().equals(user.getId())) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
        }
    }

    @Override
    public void editPicture(PictureEditRequest pictureEditRequest) {
        User user = UserThreadLocal.get();
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_LOGIN_ERROR);

        // 在此处将实体类和 DTO 进行转换
        Picture picture = new Picture();
        BeanUtils.copyProperties(pictureEditRequest, picture);
        // 注意将 list 转为 string
        picture.setTags(JSONUtil.toJsonStr(pictureEditRequest.getTags()));
        // 设置编辑时间
        picture.setEditTime(new Date());
        // 数据校验
        this.validPicture(picture);
        // 判断是否存在
        long id = pictureEditRequest.getId();
        Picture oldPicture = this.getById(id);
        ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR);
        // 校验权限
//        checkPictureAuth(oldPicture);
        // 补充审核参数
        this.fillReviewParams(picture);
        // 操作数据库
        boolean result = this.updateById(picture);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
    }

    /**
     * 根据主色调搜索图片
     *
     * @param spaceId
     * @param picColor
     * @return
     */
    @Override
    public List<PictureVO> searchPictureByColor(Long spaceId, String picColor) {
        // 1. 校验参数
        ThrowUtils.throwIf(spaceId == null || StrUtil.isBlank(picColor), ErrorCode.PARAMS_ERROR);
        User user = UserThreadLocal.get();
        // 已由注解鉴权
//        validateUserAndSpace(user, spaceId);

        // 3. 查询图片列表
        ColorUtils.Color color = ColorUtils.getColor(picColor);
        List<Picture> pictures = pictureMapper.searchPictureByColor(color.getRed(), color.getGreen(), color.getBlue(), spaceId);

        // 转换为 PictureVO
        return pictures.stream()
                .map(PictureVO::objToVo)
                .collect(Collectors.toList());
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editPictureByBatch(PictureEditByBatchRequest pictureEditByBatchRequest) {
        List<Long> pictureIdList = pictureEditByBatchRequest.getPictureIdList();
        Long spaceId = pictureEditByBatchRequest.getSpaceId();
        String category = pictureEditByBatchRequest.getCategory();
        List<String> tags = pictureEditByBatchRequest.getTags();
        // 批量重命名
        String nameRule = pictureEditByBatchRequest.getNameRule();
        User loginUser = UserThreadLocal.get();

        // 1. 校验参数
        ThrowUtils.throwIf(spaceId == null || CollUtil.isEmpty(pictureIdList), ErrorCode.PARAMS_ERROR);
        // 已有注解鉴权
//        validateUserAndSpace(loginUser, spaceId);

        String tagString = null;
        if (CollUtil.isNotEmpty(tags)) {
            tagString = JSONUtil.toJsonStr(tags);
        }
        // 3. 修改字段
        // 使用自定义的mapper方法
        pictureMapper.editPictureByBatch(spaceId, pictureIdList, category, nameRule, tagString);


        /**
         * 以下为线程池 + 分片 + 异步执行 + 同步等待代码
         *  // 分片大小（例如每片500个ID）
         *     int batchSize = 500;
         *     List<List<Long>> partitionedIds = ListUtils.partition(pictureIdList, batchSize);
         *
         *     // 使用 CompletableFuture 并发执行每个分片
         *     Executor executor = asyncTaskExecutor; // 使用 Spring 线程池
         *     List<CompletableFuture<Void>> futures = new ArrayList<>();
         *
         *     for (List<Long> subList : partitionedIds) {
         *         CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
         *             LambdaUpdateChainWrapper<Picture> updateChainWrapper = buildBaseUpdateWrapper(spaceId, subList);
         *             boolean needUpdate = false;
         *
         *             if (StrUtil.isNotBlank(category)) {
         *                 updateChainWrapper.set(Picture::getCategory, category);
         *                 needUpdate = true;
         *             }
         *
         *             if (CollUtil.isNotEmpty(tags)) {
         *                 updateChainWrapper.set(Picture::getTags, JSONUtil.toJsonStr(tags));
         *                 needUpdate = true;
         *             }
         *
         *             if (needUpdate) {
         *                 boolean result = updateChainWrapper.update();
         *                 ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
         *             }
         *         }, executor);
         *
         *         futures.add(future);
         *     }
         *
         *     // 同步等待所有任务完成
         *     CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
         * }
         */

    }

//    private void validateUserAndSpace(User loginUser, Long spaceId) {
//        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);
//        //校验空间权限
//        Space space = spaceService.getById(spaceId);
//        ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
//        if (!loginUser.getId().equals(space.getUserId())) {
//            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有空间访问权限");
//        }
//    }

    /**
     * 创建图片扩展任务
     *
     * @param createPictureOutPaintingTaskRequest
     * @return
     */
    @Override
    public CreateOutPaintingTaskResponse createPictureOutPaintingTask(CreatePictureOutPaintingTaskRequest createPictureOutPaintingTaskRequest) {
        // 获取图片信息
        Long pictureId = createPictureOutPaintingTaskRequest.getPictureId();
        Picture picture = Optional.ofNullable(this.getById(pictureId))
                .orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND_ERROR));
        // 权限校验
//        checkPictureAuth(picture);
        // 构造请求参数
        CreateOutPaintingTaskRequest taskRequest = new CreateOutPaintingTaskRequest();
        CreateOutPaintingTaskRequest.Input input = new CreateOutPaintingTaskRequest.Input();
        input.setImageUrl(picture.getSourceUrl());
        taskRequest.setInput(input);
        BeanUtil.copyProperties(createPictureOutPaintingTaskRequest, taskRequest);
        // 创建任务
        return aliYunAiApi.createOutPaintingTask(taskRequest);
    }


}




